/*
 * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package jdk.io;

import java.io.IOException;
import java.io.Reader;

/**
 * 此类实现一个字符缓冲区，可用作字符输入流。
 *
 * @author      Herb Jellinek
 * @since       JDK1.1
 */
public class CharArrayReader extends Reader {
    /** 字符缓冲区。 */
    protected char buf[];

    /** 当前缓冲区位置。 */
    protected int pos;

    /** 缓冲区中标记的位置。 */
    protected int markedPos = 0;

    /**
     * 此缓冲区末尾的索引。此索引处及之后没有有效数据。
     */
    protected int count;

    /**
     * 从指定的字符数组创建 CharArrayReader。
     * @param buf       输入缓冲区（不复制）
     */
    public CharArrayReader(char buf[]) {
        this.buf = buf;
        this.pos = 0;
        this.count = buf.length;
    }

    /**
     * 从指定的字符数组创建 CharArrayReader。
     *
     * <p> 生成的读取器将从给定的 {@code offset} 开始读取。
     * 从此读取器可读取的 {@code char} 值总数将是 {@code length} 或
     * {@code buf.length-offset} 中的较小值。
     *
     * @throws IllegalArgumentException
     *         如果 {@code offset} 为负数或大于 {@code buf.length}，
     *         或 {@code length} 为负数，或这两个值之和为负数。
     *
     * @param buf       输入缓冲区（不复制）
     * @param offset    要读取的第一个字符的偏移量
     * @param length    要读取的字符数
     */
    public CharArrayReader(char buf[], int offset, int length) {
        if ((offset < 0) || (offset > buf.length) || (length < 0) ||
            ((offset + length) < 0)) {
            throw new IllegalArgumentException();
        }
        this.buf = buf;
        this.pos = offset;
        this.count = Math.min(offset + length, buf.length);
        this.markedPos = offset;
    }

    /** 检查确保流未被关闭 */
    private void ensureOpen() throws IOException {
        if (buf == null)
            throw new IOException("Stream closed");
    }

    /**
     * 读取单个字符。
     *
     * @exception   IOException  如果发生 I/O 错误
     */
    public int read() throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (pos >= count)
                return -1;
            else
                return buf[pos++];
        }
    }

    /**
     * 将字符读入数组的一部分。
     * @param b  目标缓冲区
     * @param off  开始存储字符的偏移量
     * @param len   要读取的最大字符数
     * @return  实际读取的字符数，如果已到达流末尾则为 -1
     *
     * @exception   IOException  如果发生 I/O 错误
     */
    public int read(char b[], int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if ((off < 0) || (off > b.length) || (len < 0) ||
                ((off + len) > b.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return 0;
            }

            if (pos >= count) {
                return -1;
            }

            int avail = count - pos;
            if (len > avail) {
                len = avail;
            }
            if (len <= 0) {
                return 0;
            }
            System.arraycopy(buf, pos, b, off, len);
            pos += len;
            return len;
        }
    }

    /**
     * 跳过字符。返回实际跳过的字符数。
     *
     * <p>{@code n} 参数可能为负数，尽管在这种情况下
     * {@link Reader} 超类的 {@code skip} 方法会抛出异常。
     * 如果 {@code n} 为负数，则此方法不执行任何操作并返回 {@code 0}。
     *
     * @param n 要跳过的字符数
     * @return       实际跳过的字符数
     * @exception  IOException 如果流已关闭，或发生 I/O 错误
     */
    public long skip(long n) throws IOException {
        synchronized (lock) {
            ensureOpen();

            long avail = count - pos;
            if (n > avail) {
                n = avail;
            }
            if (n < 0) {
                return 0;
            }
            pos += n;
            return n;
        }
    }

    /**
     * 指示此流是否已准备好读取。字符数组读取器始终准备好读取。
     *
     * @exception  IOException  如果发生 I/O 错误
     */
    public boolean ready() throws IOException {
        synchronized (lock) {
            ensureOpen();
            return (count - pos) > 0;
        }
    }

    /**
     * 指示此流是否支持 mark() 操作，它确实支持。
     */
    public boolean markSupported() {
        return true;
    }

    /**
     * 标记流中的当前位置。后续对 reset() 的调用
     * 将重新定位流到此点。
     *
     * @param  readAheadLimit  在仍保留标记的情况下可读取的字符数限制。
     *                         由于流的输入来自字符数组，
     *                         因此没有实际限制；因此此参数被忽略。
     *
     * @exception  IOException  如果发生 I/O 错误
     */
    public void mark(int readAheadLimit) throws IOException {
        synchronized (lock) {
            ensureOpen();
            markedPos = pos;
        }
    }

    /**
     * 将流重置为最近的标记，如果从未标记过则重置为开头。
     *
     * @exception  IOException  如果发生 I/O 错误
     */
    public void reset() throws IOException {
        synchronized (lock) {
            ensureOpen();
            pos = markedPos;
        }
    }

    /**
     * 关闭流并释放与其关联的所有系统资源。
     * 一旦流被关闭，后续对 read()、ready()、
     * mark()、reset() 或 skip() 的调用将抛出 IOException。
     * 关闭已关闭的流不会产生任何效果。
     */
    public void close() {
        buf = null;
    }
}
